﻿    Function.prototype.bind = function () {
        if (arguments.length < 1 && typeof arguments[0] != "undefined") return this;
        var __method = this, args = [];
        for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }

        var object = args.shift();
        return function () {
            var args_to_apply = []

            for (var i = 0; i < args.length; i++) { args_to_apply.push(args[i]); }
            for (var i = 0; i < arguments.length; i++) { args_to_apply.push(arguments[i]); }
            return __method.apply(object, args_to_apply);
        }
    };

    var DebugLine = function () {
        if (console) {
            var f = console.debug || console.write;
            if (f) {
                for (var i = 0; i < arguments.length; i++) {
                    f(arguments[i]);
                }
            }
        }
    }

    function BrowserIsChrome() {
        return /Chrome/.test(navigator.userAgent);    
    }

    function BrowserIsFirefox() {
        return /Firefox/.test(navigator.userAgent);
    }

    function GetFirefoxVersion() {
        var regexp = /Firefox\/(\d{1,5}\.\d{1,5})/;
        regexp.ignoreCase = true;
        regexp.multiline = false;
        var m = regexp.exec(navigator.userAgent);
        if (m != 'undefined' && m != 'null' && m.hasOwnProperty("length") && m.length < 2) {
            return 0.0;
        }
        try {
            return parseFloat(m[1]) || 0.0;
        }
        catch (err) {
            return 0.0;
        }
    }

    var DND_UPLOAD_GLOBALS_ProgressCalculators = {
        isChrome: BrowserIsChrome(),
        isFirefox: BrowserIsFirefox(),
        isFireFoxNew: BrowserIsFirefox() ? GetFirefoxVersion() >= 4.0 : false,
        NTLMChrome: function (line, progressInfo) {
            var prevProgress = line.prevProgress || 0;
            var baseValue = line.progressBase || 0;
            if (prevProgress > progressInfo.loaded) {
                baseValue = 50;
                line.progressBase = 50;
            }
            line.prevProgress = progressInfo.loaded;
            var result = (baseValue + Math.round((progressInfo.loaded / progressInfo.total) * 50));
            if (baseValue > 0 && result <= 50) {
                result += 50;
            }
            return result;
        },
        NTLMFirefox: function (line, progressInfo) {
            if (this.isFireFoxNew) {
                return this.CLASSIC(line, progressInfo);
            }
            //DebugLine("initial progress is " + progressInfo.loaded + " of "+ progressInfo.total);
            var prevProgress = line.prevProgress || 0;
            var progressCoeff = line.progressCoeff || 1;
            var baseValue = line.progressBase || 0;
            if (prevProgress >= progressInfo.loaded || progressInfo.loaded > progressInfo.total) {
                baseValue = 50;
                line.progressBase = 50;
            }
            while (progressInfo.loaded > progressInfo.total) {
                progressInfo.loaded -= progressInfo.total;
                baseValue = 50;
                line.progressBase = 50;
            }
            line.prevProgress = progressInfo.loaded;
            var result = (baseValue + Math.round((progressInfo.loaded / progressInfo.total) * 50));
            if (baseValue > 0 && result <= 50) {
                result += 50;
            }
            //DebugLine("output progress is " + result + "%");
            return result;
        },
        NTLM: function (line, progressInfo) {
            if (this.isChrome) {
                return this.NTLMChrome(line, progressInfo);
            }
            if (this.isFirefox) {
                return this.NTLMFirefox(line, progressInfo);
            }
            return this.CLASSIC(line, progressInfo);
        },
        CLASSIC: function (line, progressInfo) {
            return Math.round((progressInfo.loaded / progressInfo.total) * 100);
        },
        DEFAULT: function (line, progressInfo) {
            return Math.round((progressInfo.loaded / progressInfo.total) * 100);
        }

    };


    function DropAdapter(targetInfo, options) {
        var result = {
            progressMode: 'DEFAULT',
            target: targetInfo,
            webPart: $('#' + targetInfo.WPCellID),
            active: false,
            filesInProgressCount: 0,
            linesContainer: null,
            fileLines: new Array(),
            progressUI: null,
            limits: { MaxFileSize: 0, BlokedFileTypes: ["exe", "bat", "dll", "com", "ocx"] },
            dropArrowVisible: false,
            hidingTimeout: null,
            title: 'Drop files into {target.title}! New files will be visible after a automatic page refresh',
            dropableTokenTooltip: 'This library supports uplloading by Drag and Drop',
            onFileUploadStarting: function (fileLine) { },
            onFileUploadFinished: function (fileLine) { },
            onFileUploadError: function (fileLine) { },
            isFileDrop: function (jqEvent) {
                var dt = jqEvent.originalEvent.dataTransfer;
                if (!dt) return false;
                //FF
                if (dt.types.contains && !dt.types.contains("Files")) return false;
                //Chrome
                if (dt.types.indexOf && dt.types.indexOf("Files") == -1) return false;


                return true;
            },
            extractFiles: function (jqEvent) {
                var dt = jqEvent.originalEvent.dataTransfer;
                if (!dt) {
                    return null;
                }
                return dt.files;
            },
            setDropEffect: function (jqEvent) {
                var dt = jqEvent.originalEvent.dataTransfer;
                if ($.browser.webkit) dt.dropEffect = 'copy';
            },
            ensureProgressUIVisible: function (elem, _hideDropArrow) {
                var t = this;
                wp = $(elem);
                if (t.active == false) {
                    if (t.progressUI == null) {
                        t.createProgressUI();
                    }
                    t.dropableToken.hide();
                    t.progressUI.width(t.webPart.width());
                    t.progressUI.height(t.webPart.height());
                    t.progressUI.fadeIn(250);
                    t.linesContainer.fadeIn(250);
                    if (_hideDropArrow && (_hideDropArrow === true)) {
                        t.hideDropArrow();
                    }
                    else
                        t.showDropArrow();
                    t.active = true;
                }
            },
            hideProgressUI: function () {
                var t = this;
                if (t.active == false) {
                    return;
                }
                if (t.progressUI != null) {
                    t.progressUI.fadeOut(250);
                }
                if (t.linesContainer != null) {
                    t.linesContainer.fadeOut(250);
                }
                t.active = false;
                setTimeout(function () { t.dropableToken.show(); }, 250);
            },
            showDropArrow: function () {
                var t = this;
                if (t.dropArrowVisible === false) {
                    t.fileListWrapper.addClass('file-list-wrapper').removeClass('file-list-wrapper-noarrow');
                    t.dropArrowVisible = true;
                }
            },
            hideDropArrow: function () {
                var t = this;
                if (t.dropArrowVisible === true) {
                    t.fileListWrapper.removeClass('file-list-wrapper').addClass('file-list-wrapper-noarrow')
                    t.dropArrowVisible = false;
                }
            },

            createProgressUI: function () {
                var t = this;
                t.webPart.addClass("progressUIContainer");
                t.webPart.prepend(
                      "<div class='progressUI' style='display:none;'>"
                        + "<div class='file-list-wrapper'>"
                        + "<div class='file-list'></div>"
                        + "</div>"
                        + "</div>");
                t.progressUI = $(".progressUI", t.webPart);
                t.progressUI.width(t.webPart.width());
                t.progressUI.height(t.webPart.height());
                t.fileListWrapper = $('.file-list-wrapper');
                $('.caption .token', t.progressUI).text(t.title);
                t.linesContainer = $(".file-list", t.webPart);
                t.webPart.append("<div class='dropableToken'>&nbsp;</div>");
                t.dropableToken = $(".dropableToken", t.webPart);
                t.dropableToken.attr("title", t.dropableTokenTitle);
            },

            blockedLinesCounter: 0,

            addBlockedFileItem: function (fileObj, reason) {
                var t = this;
                var fileName = (fileObj.name || fileObj.fileName);
                var linesCount = t.blockedLinesCounter;
                var blockedLineID = "${idPreffix}_blocked_fileLine${id}"
                         .replace("${id}", linesCount)
                         .replace("${idPreffix}", t.target.WPCellID);
                var html = ("<div class='b-file' id='${blockedlineId}'>" +
							"<div class='link'>${name}</div>" +
                            "<span class='blokedicon'></span>" +
                            "<div class='result' ></div>"
						 + "</div>")
                         .replace("${name}", fileName)
                         .replace("${blockedlineId}", blockedLineID);

                t.linesContainer.append(html);
                var lineHtml = $('#' + blockedLineID, t.linesContainer);
                var reasonHtml = $('.result', lineHtml);
                reasonHtml.html((reason || "Error").toString());

            },

            addFileItem: function (fileObj) {
                var t = this;
                t.filesInProgressCount++;
                var fileName = (fileObj.name || fileObj.fileName);
                var linesCount = t.fileLines.length;
                var fileLineID = "${idPreffix}_fileLine${id}"
                         .replace("${id}", linesCount)
                         .replace("${idPreffix}", t.target.WPCellID);

                var html = ("<div class='b-file' id='${lineid}'>" +
							"<div class='link'><a>${name}</a></div>" +
							"<div contenteditable='true' class='url'></div>" +
							"<div class='loader'><div class='progress-value'></div></div>" +
                            "<div class='result' ></div>"
						 + "</div>")
                         .replace("${name}", fileName)
                         .replace("${lineid}", fileLineID);

                t.linesContainer.append(html);

                var fileLine = $('#' + fileLineID, t.linesContainer);
                fileLine.progressBar = $(".loader", fileLine);
                fileLine.progressMeter = $(".loader .progress-value", fileLine);
                fileLine.resultText = $(".result", fileLine);
                fileLine.file = fileObj;
                fileLine.orderIndex = linesCount;
                fileLine.owner = t;
                t.fileLines.push(fileLine);
                return fileLine;
            },

            showProgress: function (fileLine, e, completed) {
                var t = this;
                var value = '';
                //if (completed === true) {
                //    value = "100%";
                //}
                //else
                if (e.lengthComputable) {
                    var proressInfo = { loaded: e.loaded, total: e.total };
                    value = DND_UPLOAD_GLOBALS_ProgressCalculators[t.progressMode](fileLine, proressInfo) + "%";
                }
                if (value != '') {
                    fileLine.progressMeter.width(value);
                }
            },
            freeLine: function (line) {
                var t = line.owner;
                t.filesInProgressCount--;
                setTimeout(function () {
                    line.fadeOut(500);
                    setTimeout(function () {
                        line.owner = null;
                        line.remove();
                        line = null;
                        if (t.filesInProgressCount == 0) {
                            t.hideProgressUI();
                        }
                    }, 600);
                }, 1000);
            },
            showComplete: function (line, e, err) {
                var t = line.owner;
                line.progressBar.fadeOut(300);
                if (err) {
                    line.resultText.text(err.message || "ERROR");
                    return;
                }
                else {
                    var data = $.parseJSON(e.target.responseText);
                    if (data.message) {
                        line.resultText.text(data.message);
                    }
                    if (!(data.result) || (data.result != 'ok')) {
                        t.showError(line, data);
                        return;
                    }
                }
                if (t.onFileUploadFinished) {
                    t.onFileUploadFinished(line);
                }
                line.file = null;
                t.freeLine(line);
            },
            showError: function (line, e) {
                var t = line.owner;
                line.progressBar.fadeOut(300);
                line.resultText.text(e.message || "Error");
                if (t.onFileUploadError) {
                    t.onFileUploadError(line);
                }
            },

            upload: function (fileLine) {
                var t = fileLine.owner;
                try {
                    fileLine.xhr = new XMLHttpRequest();

                    fileLine.xhr.upload.addEventListener("progress", function (e) { t.showProgress(fileLine, e, false); } .bind(t), false);

                    fileLine.xhr.onload = function (e) { t.showComplete(fileLine, e) };
                    fileLine.xhr.onerror = function (e) { t.showError(fileLine, e) };

                    fileLine.xhr.open('POST', t.target.Url + "&fileName=" + encodeURIComponent(fileLine.file.fileName || fileLine.file.name), true);
                    fileLine.xhr.send(fileLine.file);
                }
                catch (err) {
                    t.showError(fileLine, err);
                    if (t.onFileUploadError) {
                        t.onFileUploadError(fileLine);
                        return;
                    }
                }
            },

            fileIsAllowed: function (fileObj, reasonContainer) {
                try {
                    var t = this;
                    var fileSize = fileObj.size || fileObj.fileSize;
                    var fileName = "" + (fileObj.name || fileObj.fileName);
                    var lastDotPos = fileName.lastIndexOf('.');
                    if (fileSize == 0) {
                        reasonContainer.reason = "Empty files and folders are not allowed";
                        return false;
                    }
                    if (lastDotPos <= 0) {
                        reasonContainer.reason = "Files without extensions and folders are not allowed";
                        return false;
                    }
                    var fileExt = fileName.substr(lastDotPos + 1);
                    if (fileExt == '') {
                        reasonContainer.reason = "Files with empty extension is not allowed";
                        return false;
                    }
                    if (fileSize > (t.limits.MaxFileSize * 1024 * 1024)) {
                        reasonContainer.reason = "Files bigger than " + t.limits.MaxFileSize + " megabytes are not allowed";
                        return false;
                    }
                    for (var i = 0; i < t.limits.BlockedFileTypes.length; i++) {
                        if (fileExt == t.limits.BlockedFileTypes[i]) {
                            reasonContainer.reason = "File is blocked by system administrator";
                            return false;
                        }
                    }
                    return true;
                }
                catch (err) {
                    reasonContainer.reason = err.Message || err.message;
                    return false;
                }
            },
            startFileUpload: function (file) {
                var t = this;
                t.active = true;
                t.ensureProgressUIVisible(true);
                var item = t.addFileItem(file);
                if (t.onFileUploadStarting) {
                    t.onFileUploadStarting(item);
                }
                t.upload(item);
            },

            clearHidingTimeout: function () {
                var t = this;
                if (t.hidingTimeout != null) {
                    clearTimeout(t.hidingTimeout);
                    t.hidingTimeout = null;
                }
            },
            setHidingTimeout: function () {
                var t = this;
                t.clearHidingTimeout();
                t.hidingTimeout = setTimeout(function () {
                    if (t.filesInProgressCount == 0) {
                        t.hideProgressUI();
                    }
                }, 100);
            },
            onDragEnter: function (jqEvent, elem) {
                var t = this;
                t.clearHidingTimeout();
                var allow = t.isFileDrop(jqEvent);
                if (allow === true) {
                    t.ensureProgressUIVisible(elem);
                }
                return true;
            },
            onAreaDragEnter: function (jqEvent, elem) {
                var t = this;
                t.clearHidingTimeout();
                var allow = t.isFileDrop(jqEvent);
                if (allow === true) {
                    t.showDropArrow();
                    return;
                }
                return true;
            },

            onAreaDragLeave: function (jqEvent, elem) {
                var t = this;
                t.setHidingTimeout();
                t.hideDropArrow();
                return false;
            },
            onAreaDragOver: function (jqEvent, elem) {
                var t = this;
                t.clearHidingTimeout();
                if (t.isFileDrop(jqEvent)) {
                    t.setDropEffect(jqEvent);
                    return false;
                }
            },
            onAreaDragDrop: function (jqEvent, elem) {
                var t = this;
                t.clearHidingTimeout();
                if (t.isFileDrop(jqEvent)) {
                    var files = t.extractFiles(jqEvent);
                    for (var index = 0; index < files.length; index++) {
                        var f = files[index];
                        if (f instanceof File) {
                            reasonArgs = { reason: '' };
                            if (true === t.fileIsAllowed(f, reasonArgs)) {
                                t.startFileUpload(f);
                            }
                            else {
                                t.addBlockedFileItem(f, reasonArgs.reason);
                            }
                        }
                    }
                    t.hideDropArrow();
                }
                return false;
            },
            onAreaDragEnd: function () {
                t.hideDropArrow();
            }
        };
        $.extend(result, options);
        if (result.target.Title && result.title) {
            result.title = result.title.replace("{target.title}", result.target.Title);
        }
        result.createProgressUI();
        result.webPart.bind('dragenter', function (jqEvent) { return result.onDragEnter(jqEvent, result); });
        result.progressUI.bind('dragenter', function (jqEvent) { return result.onAreaDragEnter(jqEvent, result); });
        result.progressUI.bind('dragleave', function (jqEvent) { return result.onAreaDragLeave(jqEvent, result); });
        result.progressUI.bind('dragover', function (jqEvent) { return result.onAreaDragOver(jqEvent, result); });
        result.progressUI.bind('drop', function (jqEvent) { return result.onAreaDragDrop(jqEvent, result); });
        result.progressUI.bind('dragend', function (jqEvent) { return result.onAreaDragEnd(jqEvent, result); });

        result.webPart.addClass("progressUIContainer");
        return result;



    }

function BlockDocumentDrop() {
    $(document)
	        .bind('dragenter', function (e) { return false; })
	        .bind('dragleave', function (e) { return false; })
	        .bind('dragover', function (e) {
	            var dt = e.originalEvent.dataTransfer;
	            if (!dt) { return; }
	            dt.dropEffect = 'none';
	            return false;
	        });
}
